import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:sleep/model/blue/blue_model.dart';
import 'package:sleep/model/blue_connection_state.dart';
import 'package:sleep/provider/blue/blue_connector.dart';
import 'package:sleep/provider/db_provider.dart';

const String kServer = "fff0";
const String kWriteCharacteristic = "fff2";
const String kNotifaCharacteristic = "fff1";

class BlueNotifier extends ChangeNotifier {
  final BlueConnector connector = BlueConnector(FlutterReactiveBle());
  final FlutterReactiveBle ble = FlutterReactiveBle();
  final StreamController<List<double>> _controller =
      StreamController.broadcast();

  final String blueKey;
  BlueModel? blue;
  Stream<List<double>> get state => _controller.stream;
  CustomQualified? _qualified;
  StreamSubscription? _subscription;
  String bufStr = "";
  BlueNotifier(this.blue, this.blueKey) {
    listenBlueState();
    listenBlueConnctState();
  }

  listenBlueState() {
    ble.statusStream.listen((event) {
      switch (event) {
        case BleStatus.ready:
          {
            if (blue != null) {
              connectBlue(blue!);
            }
            break;
          }

        default:
          break;
      }
    });
  }

  listenConnectState() {
    connector.state.listen((connectModel) {
      blue = blue?.copyWith(connectionState: connectModel.connectionState);
      if (blue?.connectionState == BlueConnectionState.connected) {
        //连接成功
        subScribe();
      }
      notifyListeners();
    });
  }

  Map<DeviceConnectionState, BlueConnectionState> stateMap = {
    DeviceConnectionState.connected: BlueConnectionState.connected,
    DeviceConnectionState.disconnected: BlueConnectionState.disconnected,
    DeviceConnectionState.connecting: BlueConnectionState.connectting,
    DeviceConnectionState.disconnecting: BlueConnectionState.disconnecting,
  };

  listenBlueConnctState() {
    ble.connectedDeviceStream.listen((event) {
      if (event.deviceId == blue?.deviceId) {
        blue = blue?.copyWith(connectionState: stateMap[event.connectionState]);
        if (event.connectionState == DeviceConnectionState.connected) {
          subScribe();
        }
        notifyListeners();
      }
    });
  }

  connectBlue(BlueModel blueModel) {
    if (blueModel.connectionState == BlueConnectionState.disconnected ||
        blueModel.connectionState == null) {
      blue = blueModel;
      connector.connect(blueModel);
      notifyListeners();
    }
  }

  disConnectBlue() {
    if (blue == null) return;
    connector.disconnect(blue!);
    _subscription?.cancel();
    blue = null;
  }

  subScribe() async {
    if (blue == null) return;
    _qualified = await getcharacteristic(blue!.deviceId);
    _subscription = ble
        .subscribeToCharacteristic(_qualified!.notifier)
        .listen(dataStream, onError: (e) {
      print(e);
    }, onDone: () {
      print('done');
    });
  }

  dataStream(List<int> data) {
    final List<double> revData = [];
    final RegExp reg = RegExp(r'[0-9]*,');

    String listString = String.fromCharCodes(data);

    final matchString = bufStr + listString;

    final matches = reg.allMatches(matchString);
    int endIndex = 0;
    for (Match match in matches) {
      revData.add(
          double.tryParse(match.group(0)?.replaceAll(',', '') ?? '0') ?? 0);
      endIndex = match.end;
    }
    if (revData.isNotEmpty) {
      _controller.add(revData);
    }
    bufStr = matchString.substring(endIndex);
  }

  Future<CustomQualified> getcharacteristic(String device) async {
    List<Service> serviceIds = await ble.getDiscoveredServices(device);

    Service? server =
        serviceIds.where((s) => s.id.toString().contains(kServer)).first;
    final writeCharacteristic = server.characteristics
        .where(
          (c) => c.id.toString().contains(kWriteCharacteristic),
        )
        .first;

    final notifaCharacteristic = server.characteristics
        .where(
          (c) => c.id.toString().contains(kNotifaCharacteristic),
        )
        .first;

    final qwritecharacteristic = QualifiedCharacteristic(
        serviceId: server.id,
        characteristicId: writeCharacteristic.id,
        deviceId: device);

    final qnotifacharacteristic = QualifiedCharacteristic(
        serviceId: server.id,
        characteristicId: notifaCharacteristic.id,
        deviceId: device);

    return CustomQualified(qwritecharacteristic, qnotifacharacteristic);
  }

  @override
  void dispose() {
    disConnectBlue();
    super.dispose();
  }
}

class CustomQualified {
  final QualifiedCharacteristic write;
  final QualifiedCharacteristic notifier;

  CustomQualified(this.write, this.notifier);
}

final sleepBlueProvider = ChangeNotifierProvider<BlueNotifier>((ref) {
  final db = ref.read(dbProvider);
  final sleepBlue = db.getBlueModel(kSleepBlue);
  return BlueNotifier(sleepBlue, kSleepBlue);
});

final faigueBlueProvider = ChangeNotifierProvider<BlueNotifier>((ref) {
  final db = ref.read(dbProvider);
  final faigueBlue = db.getBlueModel(kFaigueBlue);
  return BlueNotifier(faigueBlue, kFaigueBlue);
});

final plueBlueProvider = ChangeNotifierProvider<BlueNotifier>((ref) {
  final db = ref.read(dbProvider);
  final plueBlue = db.getBlueModel(kPlueBlue);
  return BlueNotifier(plueBlue, kPlueBlue);
});
